package esrc.c9cf.schedulers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.realtime.AbsoluteTime;
import javax.realtime.Clock;
import javax.realtime.HighResolutionTime;
import javax.realtime.NativeHelper;
import javax.realtime.PeriodicParameters;
import javax.realtime.PriorityParameters;
import javax.realtime.RealtimeThread;
import javax.realtime.RelativeTime;
import javax.realtime.ReleaseParameters;
import javax.realtime.Schedulable;
import javax.realtime.SchedulingParameters;
import javax.realtime.RealtimeThread.RealtimeThreadState;

import esrc.c9cf.Scheduler;

public class PartitionedEarliestDeadlineFirst extends Scheduler {
	private static final int MAX_PRIORITY = NativeHelper.getMaxRTPriority();
	private static final int MIN_PRIORITY = NativeHelper.getMinRTPriority();
	private static final int NORM_PRIORITY = MIN_PRIORITY + (MAX_PRIORITY-MIN_PRIORITY)/2;
	private static int basePriority = NORM_PRIORITY;
	
	private static PartitionedEarliestDeadlineFirst instance = new PartitionedEarliestDeadlineFirst();
	
	protected PartitionedEarliestDeadlineFirst() {
		super();
	}
	public static PartitionedEarliestDeadlineFirst instance()	{
		return instance;
	}
	
	public static void setBasePriority(int priority) {
		basePriority = priority;
	}
	public static int getBasePriority()	{
		return basePriority;
	}
	@Override
	public void reschedule(AbsoluteTime theTime, Schedulable schedulable) {
		ArrayList<RealtimeThread> readyQ = new ArrayList<RealtimeThread>();
		
		synchronized(feasibilitySet)
		{
			for(Schedulable schedulables_in_the_set : feasibilitySet)
			{
				if(schedulable instanceof RealtimeThread)
				{
					RealtimeThread schedulable_rtt = (RealtimeThread)schedulable;
					if(schedulables_in_the_set instanceof RealtimeThread)
					{
						if(schedulable_rtt.getSchedulingParameters() != null) throw new RuntimeException();
						RealtimeThread rtt_set = (RealtimeThread) schedulables_in_the_set;
						int rtt_cpu = schedulable_rtt.getCurrentCPU();
						int rtt_set_cpu = rtt_set.getCurrentCPU();
						if(rtt_cpu == rtt_set_cpu)
						{
							if(rtt_set.getReleaseParameters() instanceof PeriodicParameters)
							{
								//System.out.println("time "+theTime+ ", tid:"+rtt_set.getTID()+", state:"+rtt_set.getRealtimeThreadState().toString()+", nextReleaseTime: "+ rtt_set.getNextReleaseTime());
								if((rtt_set.getRealtimeThreadState() == RealtimeThreadState.RTT_WAITING) && (theTime.compareTo(rtt_set.getNextReleaseTime()) >= 0))
								{
									rtt_set.setRealtimeThreadState(RealtimeThreadState.RTT_READY);
									readyQ.add(rtt_set);
								}
							}
						}
					}
				}
			}
			
			// sort by deadline, descending + nextReleaseTime, ascending
			class RTTComparator implements Comparator<RealtimeThread> {

				@Override
				public int compare(RealtimeThread arg0, RealtimeThread arg1) {
					int releaseTimeCompareVal = arg1.getNextReleaseTime().compareTo(arg0.getNextReleaseTime());
					long rateCompareVal = 0;
					ReleaseParameters param0 = arg0.getReleaseParameters();
					ReleaseParameters param1 = arg1.getReleaseParameters();
					if(param0 instanceof PeriodicParameters && param1 instanceof PeriodicParameters)
					{
						RelativeTime period0 = ((PeriodicParameters)param0).getPeriod();
						RelativeTime period1 = ((PeriodicParameters)param1).getPeriod();
						rateCompareVal = period0.compareTo(period1);		// smaller period = faster rate
					}
					
					if(rateCompareVal == 1) return 1;
					else if(rateCompareVal == 0) return releaseTimeCompareVal;
					else return -1;
				}
			}
			Collections.sort(readyQ, new RTTComparator());
			for(RealtimeThread rt : readyQ)
			{
				Dispatcher.Dispatch(rt, rt.getCurrentCPU());
			}
		}
	}

	@Override
	public void fireSchedulable(Schedulable schedulable) {
		throw new UnsupportedOperationException();
	}
	@Override
	public ReleaseParameters getDefaultReleaseParameters() {
		//TODO: will return adequate one when AperiodicParameters is available 
		return null;
	}
	@Override
	public SchedulingParameters getDefaultSchedulingParameters() {
		return new PriorityParameters(basePriority, this);
	}
	@Override
	public String getPolicyName() {
		return "Partitioned Earlist Deadline First";
	}
	@Override
	public boolean isFeasible() {
		// TODO Auto-generated method stub
		return false;
	}
	@Override
	public void addToFeasibilitySet(Schedulable schedulable)
	{
		if(schedulable instanceof RealtimeThread)
		{
			RealtimeThread rtt = (RealtimeThread)schedulable;
			synchronized(feasibilitySet)
			{
				//feasibilitySet.add(schedulable);
				for(int i = 0; i < feasibilitySet.size(); i++)
				{
					RealtimeThread rtt_set = (RealtimeThread)feasibilitySet.get(i);
					AbsoluteTime abs_set = rtt_set.getNextReleaseTime();
					if(abs_set == null) 
				}
			}
		}
	}
}
